Skip to content

Add Decimal32, Decimal64, Decimal128#100729

Merged
tannergooding merged 126 commits into
dotnet:mainfrom
RaymondHuy:issue-81376
Jul 3, 2026
Merged

Add Decimal32, Decimal64, Decimal128#100729
tannergooding merged 126 commits into
dotnet:mainfrom
RaymondHuy:issue-81376

Conversation

@RaymondHuy

Copy link
Copy Markdown
Contributor

Resolve #81376

@ghost

ghost commented Apr 6, 2024

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@RaymondHuy
RaymondHuy marked this pull request as draft April 6, 2024 17:29
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Apr 6, 2024
Comment thread src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/Numerics/Decimal128.cs Outdated
Comment thread src/libraries/System.Private.CoreLib/src/System/Number.DecimalIeee754.cs Outdated
@tannergooding

Copy link
Copy Markdown
Member

This is on my radar to take a look at; just noting it might be a bit delayed due to other priorities for the .NET 9 release.

CC. @jeffhandley

@RaymondHuy
RaymondHuy marked this pull request as ready for review April 9, 2024 17:21
@RaymondHuy

Copy link
Copy Markdown
Contributor Author

@tannergooding

Copy link
Copy Markdown
Member

@RaymondHuy looks like there's still a couple test failures:

System.Tests.Decimal128Tests.CompareTo_Other_ReturnsExpected
System.Tests.Decimal64Tests.CompareTo_Other_ReturnsExpected

Both are comparing zero (0.000.....0 vs 0.000....1) and give stack traces are similar to:

at System.UInt64.DivRem(UInt64 left, UInt64 right)
   at System.Number.<CompareDecimalIeee754>g__InternalUnsignedCompare|3_0[TDecimal,TValue](DecodedDecimalIeee754`1 current, DecodedDecimalIeee754`1 other)
   at System.Number.CompareDecimalIeee754[TDecimal,TValue](TValue currentDecimalBits, TValue otherDecimalBits)
   at System.Numerics.Decimal64.CompareTo(Decimal64 other)
   at System.Tests.Decimal64Tests.CompareTo_Other_ReturnsExpected(Decimal64 d1, Decimal64 d2, Int32 expected)
   at InvokeStub_Decimal64Tests.CompareTo_Other_ReturnsExpected(Object, Span`1)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

@tannergooding
tannergooding merged commit d116203 into dotnet:main Jul 3, 2026
140 of 144 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot modified the milestones: 11.0.0, 11.0-preview7 Jul 4, 2026
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
Resolve #81376

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Tanner Gooding <tagoo@outlook.com>
Co-authored-by: Jeff Handley <jeffhandley@users.noreply.github.com>
@migueldecimal128

Copy link
Copy Markdown

I have independently developed decimal128 support for C# and 7 other language implementations.
I am not a C# developer, but I know a lot about decimal floating point ... implementation and validation.
If I can be of assistance in helping you folks bring decimal floating point to the world then pls advise.

decimal128.com
https://github.com/migueldecimal128/decimal128-csharp

@tannergooding

Copy link
Copy Markdown
Member

Thanks for the offer. I think we've got it covered for .NET 11 and have used the well tested Intel DFP as the foundational starting point, plus I've already done some optimization work on top.

There will be more opportunities for tuning and improving things in .NET 12+ and you're welcome to contribute there, just make sure to open an issue and discuss large changes or refactorings first.

@migueldecimal128

Copy link
Copy Markdown

Thank you for your prompt response.
The Intel DFP code is ... uhh ... interesting.
If I may offer some unsolicited advice regarding conformance validation ...

  • use the Intel test vectors
  • use the Cowlishaw/IBM decTest test vectors
  • use the IBM FPtest test vectors which were developed for IBM hardware verification

IEEE 754-2019 leaves some aspects of NaN handling unspecified. I chose to use Cowlishaw GDAS behavior.

I am very glad you folks are doing this. :)

@tannergooding

Copy link
Copy Markdown
Member

We're already using the Intel test vectors from their libraries, as well as a general oracle test bed for ad-hoc local validation beyond the general inner loop validation that runs as part of every CI, which particularly cover edge case behaviors, cohort handling, NaN handling, etc

I would be interested in what aspects of NaN you believe are under-specified. The spec has pretty strict requirements around the handling of NaNs, canonicalization to the preferred exponent, recommended (but not required) propagation of canonical payloads, handling of signaling vs quiet NaNs, etc.

The areas that are open ended tend to have the same general recommendations as binary floating-point numbers and the same official recommendations on how they should be handled.

@migueldecimal128

Copy link
Copy Markdown

I would be interested in what aspects of NaN you believe are under-specified.
6.2.3 NaN propagation
... This standard does not specify which of
the input NaNs will provide the payload.

Intel handles this differently from Cowlishaw/GDAS.

@tannergooding

Copy link
Copy Markdown
Member

That is the same area as binary floating-point, which is that when 2 or more NaN inputs are present, the precise NaN returned is undefined (and often deviates across platforms).

Which is returned does not matter and implementations should not be dependent on it, again same as with binary floating-point (and will often trivially be broken by basic compiler or hardware optimizations, such as allowing x + y to optimize to y + x, as relying on commutativity is a legal and standard IEEE 754 optimization, unlike relying on associativity which is not safe).

In .NETs case, we prefer the first occurring NaN, which matches how most hardware handles binary floating-point as well.

@migueldecimal128

Copy link
Copy Markdown

which matches how most hardware handles binary floating-point as well.
I suspect (but do not have direct evidenc) that IBM System Z hardware for decimal floating point follows Cowlishaw/IBM NaN propagation rules. sNaN payload takes precedence.

In .NETs case, we prefer the first occurring NaN,

If you folks are going to take the payload of the first NaN then I will consider changing my library to do the same. Admittedly NaN payload is not a very big deal, but I think we can agree that it would be best for decimal floating point behavior to be the same across platforms.

Taking the first prob would simplify things (slightly) for fma.

@tannergooding

tannergooding commented Jul 20, 2026

Copy link
Copy Markdown
Member

but I think we can agree that it would be best for decimal floating point behavior to be the same across platforms

Not really, IMO. Because it is already inconsistent across platforms and has been for 40 years. It is even inconsistent within a single platform due to legal and common optimizations, even when following strict IEEE 754 compliance mode with propagation (again, because compilers often trivially change x + y into y + x

Code should in general just be checking for IsNaN, not a specific NaN. They might rely on one of the NaNs they input getting propagated back out, but cannot strictly rely on which NaN when both inputs are NaN.

-- Notably this also means that the .NET implementation is not strictly first NaN to what the user wrote either, its rather what ends up as the first NaN at the point the comparison is done, which for a pure software implementation is "likely" the first NaN the user input, but which may differ based on API depending on what is convenient or simple. -- This is also why hardware tends to propagate the first NaN, because both inputs may have to be checked and its cheaper to check one and exit early than check both and decide which of either.

@migueldecimal128

Copy link
Copy Markdown

good points

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Numerics community-contribution Indicates that the PR has been added by a community member new-api-needs-documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API Proposal]: Add Decimal32, Decimal64, and Decimal128 from the IEEE 754-2019 standard.